package com.keqiang.chart.impl.axis.render

import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PathEffect
import android.graphics.RectF
import android.text.Layout
import android.text.TextPaint
import com.keqiang.chart.impl.axis.BaseAxisChart
import com.keqiang.chart.impl.axis.BaseXAxis
import com.keqiang.chart.impl.axis.Label
import com.keqiang.chart.utils.ObjectPool
import com.keqiang.utils.PaintUtils

/**
 * X轴绘制器
 *
 * @param drawLabelByGridLine 标签是否与网格线水平居中对齐，如果为false，则标签与网格中心点水平居中对齐
 *
 * @author Created by wanggaowan on 2022/7/20 09:45
 */
open class XAxisRender<T : BaseAxisChart<*>> @JvmOverloads constructor(
    val chart: T, val drawLabelByGridLine: Boolean = true) : IXAxisRender {

    override fun onDraw(canvas: Canvas, start: Float, top: Float, end: Float, bottom: Float,
                        gridWidth: Float, labelMaxHeight: Float, yAxisRange: Float) {
        val textPaint = ObjectPool.obtainPaint<TextPaint>(true)
        val paint = ObjectPool.obtainPaint<Paint>()
        val tempRectF = ObjectPool.obtainRectF()
        val path = ObjectPool.obtainPath()

        // 绘制轴线
        val xAxis = chart.xAxis
        val yAxis = chart.yAxis
        setPaint(paint, xAxis.lineColor, xAxis.lineWidth, xAxis.linePathEffect)
        val y = top + (bottom - top) / 2f
        path.moveTo(start, y)
        path.lineTo(end, y)
        // 只有drawPath，给画笔设置的PathEffect才生效
        canvas.drawPath(path, paint)
        xAxis.drawRectF.set(start, top, end, bottom)

        // 绘制轴线文本
        var tempStart = start + xAxis.startPadding
        textPaint.textSize = xAxis.labelTextSize
        textPaint.color = xAxis.labelColor
        setPaint(paint, xAxis.gridLineColor, xAxis.gridLineWidth, xAxis.gridLinePathEffect)

        xAxis.labelList?.let {
            for (index in it.indices) {
                if (gridWidth > 0 && labelMaxHeight > 0) {
                    if (drawLabelByGridLine) {
                        drawLabelByGridLine(canvas, textPaint, index, it, xAxis, gridWidth, bottom, end,
                            labelMaxHeight, tempStart, tempRectF)
                    } else {
                        drawLabelByGridCenter(canvas, textPaint, index, it, xAxis, gridWidth, bottom,
                            labelMaxHeight, tempStart, tempRectF)
                    }
                }

                if (index > 0 && xAxis.drawGridLine) {
                    // 绘制X轴网格线，如果xAxis.gridUseEndPadding为true，则X轴结束点实际都在Y轴最后一根轴线下面
                    path.reset()
                    path.moveTo(tempStart,
                        if (xAxis.gridUseEndPadding) top - yAxisRange + yAxis.endPadding else top - yAxisRange)
                    path.lineTo(tempStart, if (xAxis.gridUseStartPadding) top - yAxis.startPadding else top)
                    canvas.drawPath(path, paint)
                }

                tempStart += gridWidth
            }
        }

        if (!drawLabelByGridLine && xAxis.drawGridLine) {
            // isDrawLabelByGridLine为false，此时标签与网格中心对齐，网格线要多画一条

            // 绘制X轴网格线，如果xAxis.gridUseEndPadding为true，则X轴结束点实际都在Y轴最后一根轴线下面
            path.reset()
            path.moveTo(tempStart,
                if (xAxis.gridUseEndPadding) top - yAxisRange + yAxis.endPadding else top - yAxisRange)
            path.lineTo(tempStart, if (xAxis.gridUseStartPadding) top - yAxis.startPadding else top)
            canvas.drawPath(path, paint)
        }

        ObjectPool.recycleRectF(tempRectF)
        ObjectPool.recyclePaint(paint)
        ObjectPool.recyclePaint(textPaint)
        ObjectPool.recyclePath(path)
    }

    /**
     * 绘制标签，内容与网格线水平居中对齐
     */
    protected open fun drawLabelByGridLine(canvas: Canvas, textPaint: TextPaint, index: Int, it: List<Label>, xAxis: BaseXAxis,
                                           gridWidth: Float, bottom: Float, end: Float, labelMaxHeight: Float, tempStart: Float,
                                           tempRectF: RectF) {
        // 绘制x轴文本
        val label = it[index]
        val text = label.value
        val labelOffset = xAxis.labelOffsetAxis

        if (!text.isNullOrEmpty() && label.weight > 0) {
            val maxWidth = gridWidth * label.weight
            if (maxWidth <= xAxis.labelHorizontalPadding) {
                // 文本绘制控件没有
                return
            }

            var labelAlign = xAxis.labelAlign
            val overAlign = xAxis.labelOverAlign
            canvas.save()

            when (index) {
                // 起始点可以向左延伸
                0 -> {
                    val leftOffset = chart.view.paddingStart.toFloat()
                    var widthHalf = maxWidth / 2f - xAxis.labelHorizontalPadding / 2f
                    val leftWidth = tempStart - chart.view.paddingStart
                    // 起始点标签绘制宽度是否与中间标签宽度一致
                    val widthEnough: Boolean
                    val maxTextWidth = if (leftWidth < widthHalf) {
                        // x轴起始标签左边需要的宽度大于实际View paddingStart到start的距离
                        widthEnough = false
                        widthHalf + leftWidth
                    } else {
                        widthEnough = true
                        maxWidth - xAxis.labelHorizontalPadding
                    }

                    // 此时宽度指定可绘制区域的宽度
                    if (!widthEnough && labelAlign == Paint.Align.CENTER) {
                        // 与轴线中心对齐
                        if (tempStart - widthHalf < leftOffset) {
                            // 标签中心与View Padding距离不够期望宽度的一半
                            widthHalf = tempStart - leftOffset
                        }

                        if (overAlign != labelAlign) {
                            if (PaintUtils.getTextLine(text, textPaint, (widthHalf * 2).toInt()) > 1) {
                                labelAlign =
                                    if (PaintUtils.getTextLine(text, textPaint, maxTextWidth.toInt()) > 1) {
                                        // 居中时可用宽度和非居中可用宽度时都超出一行，此时对齐方式使用用户设置的
                                        overAlign
                                    } else {
                                        // 居中时可用宽度超出一行但是非居中可用宽度没超出一行，
                                        // 此时对齐方式使用靠左对齐，因为此标签为起始标签
                                        Paint.Align.LEFT
                                    }
                                tempRectF.set(chart.view.paddingStart.toFloat(), bottom,
                                    tempStart + maxWidth / 2f - xAxis.labelHorizontalPadding / 2f,
                                    bottom + labelOffset + labelMaxHeight)
                            } else {
                                tempRectF.set(tempStart - widthHalf, bottom,
                                    tempStart + widthHalf, bottom + labelOffset + labelMaxHeight)
                            }
                        } else {
                            tempRectF.set(tempStart - widthHalf, bottom,
                                tempStart + widthHalf, bottom + labelOffset + labelMaxHeight)
                        }
                    } else {
                        if (xAxis.labelMaxLines > 1 && overAlign != labelAlign
                            && PaintUtils.getTextLine(text, textPaint, maxTextWidth.toInt()) > 1) {
                            labelAlign = overAlign
                        }

                        if (widthEnough) {
                            tempRectF.set(tempStart - widthHalf, bottom,
                                tempStart + widthHalf, bottom + labelOffset + labelMaxHeight)
                        } else {
                            tempRectF.set(leftOffset, bottom,
                                tempStart + widthHalf, bottom + labelOffset + labelMaxHeight)
                        }
                    }
                }

                // 结束点向右延伸
                it.size - 1 -> {
                    val centerX = end - xAxis.endPadding
                    var widthHalf = maxWidth / 2f - xAxis.labelHorizontalPadding / 2f
                    val rightWidth = xAxis.endPadding
                    // 结束点标签绘制宽度是否与中间标签宽度一致
                    val widthEnough: Boolean
                    val maxTextWidth = if (rightWidth < widthHalf) {
                        // x轴起始标签左边需要的宽度大于实际View paddingStart到start的距离
                        widthEnough = false
                        widthHalf + rightWidth
                    } else {
                        widthEnough = true
                        maxWidth - xAxis.labelHorizontalPadding
                    }

                    if (!widthEnough && labelAlign == Paint.Align.CENTER) {
                        // 与轴线中心对齐
                        if (centerX + widthHalf > end) {
                            // 标签中心与轴线结束距离不足期望宽度的一半
                            widthHalf = end - centerX
                        }

                        if (overAlign != labelAlign) {
                            if (PaintUtils.getTextLine(text, textPaint, (widthHalf * 2).toInt()) > 1) {
                                labelAlign =
                                    if (PaintUtils.getTextLine(text, textPaint, maxTextWidth.toInt()) > 1) {
                                        // 居中时可用宽度和非居中可用宽度时都超出一行，此时对齐方式使用用户设置的
                                        overAlign
                                    } else {
                                        // 居中时可用宽度超出一行但是非居中可用宽度没都超出一行，
                                        // 此时对齐方式使用靠右对齐，因为此标签为结束标签
                                        Paint.Align.RIGHT
                                    }
                                tempRectF.set(end - maxTextWidth, bottom,
                                    end, bottom + labelOffset + labelMaxHeight)
                            } else {
                                tempRectF.set(centerX - widthHalf, bottom,
                                    centerX + widthHalf, bottom + labelOffset + labelMaxHeight)
                            }
                        } else {
                            tempRectF.set(centerX - widthHalf, bottom,
                                centerX + widthHalf, bottom + labelOffset + labelMaxHeight)
                        }
                    } else {
                        if (xAxis.labelMaxLines > 1 && overAlign != labelAlign
                            && PaintUtils.getTextLine(text, textPaint, maxTextWidth.toInt()) > 1) {
                            labelAlign = overAlign
                        }

                        if (widthEnough) {
                            tempRectF.set(centerX - widthHalf, bottom,
                                centerX + widthHalf, bottom + labelOffset + labelMaxHeight)
                        } else {
                            tempRectF.set(end - maxTextWidth, bottom,
                                end, bottom + labelOffset + labelMaxHeight)
                        }
                    }
                }

                // 中间点可绘制宽度固定
                else -> {
                    val widthHalf = maxWidth / 2f - xAxis.labelHorizontalPadding / 2f
                    if (overAlign != labelAlign
                        && PaintUtils.getTextLine(text, textPaint, (maxWidth - xAxis.labelHorizontalPadding).toInt()) > 1) {
                        labelAlign = overAlign
                    }

                    tempRectF.set(tempStart - widthHalf, bottom,
                        tempStart + widthHalf, bottom + labelOffset + labelMaxHeight)
                }
            }

            canvas.clipRect(tempRectF)
            canvas.translate(tempRectF.left, bottom + labelOffset)
            val align = when (labelAlign) {
                Paint.Align.CENTER -> Layout.Alignment.ALIGN_CENTER
                Paint.Align.LEFT -> Layout.Alignment.ALIGN_NORMAL
                else -> Layout.Alignment.ALIGN_OPPOSITE
            }
            PaintUtils.drawText(canvas, textPaint, text, tempRectF.width().toInt(), xAxis.labelMaxLines, align, xAxis.labelEllipsize)
            canvas.restore()
        }
    }

    /**
     * 绘制标签，内容与网格中点水平居中对齐
     */
    protected open fun drawLabelByGridCenter(
        canvas: Canvas, textPaint: TextPaint, index: Int, it: List<Label>, xAxis: BaseXAxis,
        gridWidth: Float, bottom: Float, labelMaxHeight: Float, tempStart: Float, tempRectF: RectF,
    ) {
        // 绘制x轴文本
        val label = it[index]
        val text = label.value
        val labelOffset = xAxis.labelOffsetAxis

        if (!text.isNullOrEmpty() && label.weight > 0) {
            val maxWidth = gridWidth * label.weight
            if (maxWidth <= xAxis.labelHorizontalPadding) {
                // 文本绘制空间没有
                return
            }

            var labelAlign = xAxis.labelAlign
            val overAlign = xAxis.labelOverAlign
            canvas.save()

            if (xAxis.labelMaxLines > 1 && overAlign != labelAlign
                && PaintUtils.getTextLine(text, textPaint, maxWidth.toInt()) > 1) {
                labelAlign = overAlign
            }

            val overWidthHalf =
                (maxWidth - gridWidth - xAxis.labelHorizontalPadding) / 2f
            tempRectF.set(tempStart - overWidthHalf, bottom,
                tempStart + gridWidth + overWidthHalf, bottom + labelOffset + labelMaxHeight)

            canvas.clipRect(tempRectF)
            canvas.translate(tempRectF.left, bottom + labelOffset)
            val align = when (labelAlign) {
                Paint.Align.CENTER -> Layout.Alignment.ALIGN_CENTER
                Paint.Align.LEFT -> Layout.Alignment.ALIGN_NORMAL
                else -> Layout.Alignment.ALIGN_OPPOSITE
            }
            PaintUtils.drawText(canvas, textPaint, text, tempRectF.width().toInt(), xAxis.labelMaxLines, align, xAxis.labelEllipsize)
            canvas.restore()
        }
    }

    protected fun setPaint(paint: Paint, color: Int, strokeWidth: Float = 0f, pathEffect: PathEffect? = null) {
        paint.color = color
        paint.strokeWidth = strokeWidth
        paint.style = Paint.Style.STROKE
        paint.pathEffect = pathEffect
    }
}
